<?php


namespace coco;


use app\controller\Test;
use app\queue\Hello;
use coco\exception\HttpResponseException;
use coco\queue\Job;
use coco\traits\Singleton;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;

class Dispatch
{
    use Singleton;

    /**
     * @throws Exception
     */
    public function run()
    {
        $app = App::getInstance();
        if ($app->is_cli) {
            $command = Command::getInstance();
            $controller = $command->getController();
            if (!class_exists($controller)) {
                throw new Exception('非法控制器:' . $controller);
            }
            $method = $command->getMethod();
            $param = $command->getParam();
            $this->invoke($controller, $method, $param);
        } else {
            $request = Request::getInstance();
            $controller = $request->getController();
            if (!class_exists($controller)) {
                throw new Exception('非法控制器:' . $controller);
            }
            $method = $request->getMethod();
            try {
                $response = $this->invoke($controller, $method);
            } catch (HttpResponseException $e) {
                $response = $e->response();
            }
            if (is_null($response) || !$response instanceof Response) {
                $data = $response ?? [];
                $response = new Response();
                $response->setData($data);
            }
            $response->send();
        }
    }


    /**
     * @param $controller
     * @param $method
     * @return false|mixed
     * @throws Exception
     */
    protected function invoke($controller, $method, $param = [])
    {
        try {
            $ref_class = new ReflectionClass($controller);
            if (!$ref_class->hasMethod($method) && !$ref_class->hasMethod('__call')) {
                throw new Exception('非法操作:' . $method);
            }
            //如果存在构造方法
            if ($constructor = $ref_class->getConstructor()) {
                $argv = getMethodParams($constructor,$param);
                $controller_instance = $ref_class->newInstanceArgs($argv);
            } else {
                $controller_instance = $ref_class->newInstance();
            }
            if ($ref_class->hasMethod($method)) {
                $ref_method = $ref_class->getMethod($method);
                $argv = getMethodParams($ref_method,$param);
            } else {
                $argv = [$method, []];
            }
            $result = call_user_func_array([$controller_instance, $method], $argv);
        } catch (\ReflectionException $e) {
            throw new Exception($e->getMessage(), 0, $e);
        }
        return $result;
    }

}